Ruby 日記 17日目: クラス変数の参照と代入
以下のコードを実行するとどうなりますか
code:gold/ex17/main.rb
class S
@@val = 0
def initialize
@@val += 1
end
end
class C < S
class << C
@@val += 1
end
end
C.new
C.new
S.new
S.new
p C.class_variable_get(:@@val)
選択肢:
3と表示される
4と表示される
5と表示される
2と表示される
解説:
@@val はクラス変数
加算されるのは次の通り
クラスCの特異クラスを定義したタイミングで +1
code:rb
class C < S
class << C
@@val += 1 # この部分のこと
end
end
クラスSがnewされたタイミングで +1
クラスCがnewされたタイミングでも +1 (クラスCはクラスSを継承しているので)
C.new と S.new が2回ずつ呼ばれているので、正解は「5と表示される」だね
code:sh
# ruby gold/ex17/main.rb
5
/icons/hr.icon
クラス変数について
@@で始まる変数はクラス変数です。クラス変数はクラス定義 の中で定義され、クラスの特異メソッド、インスタンスメソッドなどから参照/ 代入ができます。
クラス変数と定数の違いは以下の通りです。
再代入可能(定数は警告を出す)
クラスの外から直接参照できない(継承されたクラスからは参照/代入可能)
比較表をつくってみるか
table:クラス変数と定数
項目 クラス変数 定数
再代入 可能 警告を出す
クラス外からの参照 不可(継承されたクラスからは参照/代入可能) 可能
クラス変数は、そのクラスやサブクラス、それらのインスタンスで共有される グローバル変数であるとみなすことができます。
code:rb
class Foo
@@foo = 1
end
class Bar < Foo
p @@foo += 1 # => 2
end
class Baz < Bar
p @@foo += 1 # => 3
end
そうだね。今回の問題はこれを理解しているかを問う問題だと思う。
モジュールで定義されたクラス変数(モジュール変数)は、そのモジュールをイ ンクルードしたクラス間でも共有されます。
code:rb
module Foo
@@foo = 1
end
class Bar
include Foo
p @@foo += 1 # => 2
end
class Baz
include Foo
p @@foo += 1 # => 3
end
なるほどね。こういう問題も出てきそう。
親クラスに、子クラスで既に定義されている同名のクラス変数を追加した場合には、 子クラスのクラス変数が上書きされます。
code:rb
class Foo
end
class Bar < Foo
@@v = :bar
end
class Foo
@@v = :foo
end
class Bar
end
参照および代入が可能なんだもんね、そうなるよね。上から順に〜って感じよね。